package requestlogs.config;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import requestlogs.model.Log;

import javax.servlet.http.HttpServletRequest;


/**
 * Controller 请求操作/记录/权鉴
 * @author wangsong
 * @mail 1720696548@qq.com
 * @date 2020/11/15 0015 11:16
 * @version 1.0.1
 */
@Slf4j
@Aspect
@Component
public class SysAspect {


    /**
     * 拦截范围
     * @param proceed
     * @return
     * @throws Throwable
     */
    @Around("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    public Object aroundGet(ProceedingJoinPoint proceed) throws Throwable {
        return run(proceed);
    }

    @Around("@annotation(org.springframework.web.bind.annotation.PostMapping)")
    public Object aroundSave(ProceedingJoinPoint proceed) throws Throwable {
        return run(proceed);
    }

    @Around("@annotation(org.springframework.web.bind.annotation.PutMapping)")
    public Object aroundUpdate(ProceedingJoinPoint proceed) throws Throwable {
        return run(proceed);
    }

    @Around("@annotation(org.springframework.web.bind.annotation.DeleteMapping)")
    public Object aroundDelete(ProceedingJoinPoint proceed) throws Throwable {
        return run(proceed);
    }

    @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public Object aroundRequest(ProceedingJoinPoint proceed) throws Throwable {
        return run(proceed);
    }

    @Autowired
    private HttpServletRequest request;


    /**
     * 数据请求入口
     */
    private Object run(ProceedingJoinPoint proceed) throws Throwable {
        Log logs = this.requestLogCollectAndPrint(proceed, request);
        Object respData = null;
        try {
            respData = proceed.proceed();
        } catch (Exception e) {
            e.printStackTrace();
            return "error";
        }
        try {
            logs.setResponseData(respData.toString());
        } catch (Exception e) {
            //不管
        }
       return respData;
    }

    /**
     * 请求日志收集 / 打印日志到控制台
     * <P>
     *  如需统一日志收集,在此收集内容到统一日志收集器中
     * </P>
     * @param request 改方法为异步执行,request 必须调用方传递,也无法在 proceed中获取
     * @author wangsong
     * @mail 1720696548@qq.com
     * @date 2020/10/28 0028 15:04
     * @version 1.0.1
     */
    private Log requestLogCollectAndPrint(ProceedingJoinPoint proceed, HttpServletRequest request) {
        String serverName = request.getServerName();          // 获取域名(服务器路径)
        String referer = request.getHeader("referer");     // 请求来源(发起者当前页面路径)
        String ip = getIpAddress(request);                   // 获取用户真实ip(发起者)
        String uri = request.getRequestURI();                // 请求行中的接口名称
        String method = request.getMethod();                 // 请求方式(get/post)
        String url = request.getRequestURL().toString();     // 获得客户端发送请求的完整url
        String host = request.getRemoteHost();               // 请求的客户机的主机名
        int port = request.getRemotePort();                  // 请求的客户机的端口号
        // 请求的类名
        String className = proceed.getTarget().getClass().getName();
        // 请求的包名
        String packageName = proceed.getTarget().getClass().getPackage().getName();

        // 如果是文件不调用 proceed.getArgs() 读取数据
        Object[] args = null;
        String contentType = request.getContentType();
        String contentTypeFile = "multipart/form-data";
        if (contentType != null && contentType.contains(contentTypeFile)) {
            args = new Object[]{"文件无法解析"};
        } else {
            args = proceed.getArgs();
        }
        // uri ： 接口  包： packageName,  请求类： 接口+类描叙+接口描叙
        // 记录日志信息
        Log log = new Log();
        log.setReferer(referer);
        log.setUrl(url);
        log.setUri(uri);
        log.setIp(ip);
        log.setHost(host);
        log.setMethod(method);
        log.setServerName(serverName);
        log.setPort(port + "");
        log.setPackageName(packageName);
        log.setClassName(className);

        // 获取请求人信息 , 通过登录 token 直接走 token 中获取用户信息 (jwt 直接token中拿，其他redis 中拿即可)
        //================================================
        //==================== **** =====================
        //================================================
        log.setFullName("小二");
        log.setUserId("1");

        // 默认失败,接口访问成功后修改为成功
        log.setState(0);
        // 请求数据
        try {
            if (args != null) {
                String jsonParam = JSON.toJSONString(args);
                log.setRequestData(jsonParam);
            }
        } catch (Exception e) {
            log.setRequestData("the request data cannot be parsed");
        }
        // 打印请求日志
        this.printLog(log);
        return log;
    }




    /**
     * 获取请求地址
     * @author wang-song
     * @param request
     * @date 2020/7/14 0014 14:16
     * @return java.lang.String
     * @version 1.0.1
     */
    private String getIpAddress(HttpServletRequest request) {
        String unknown = "unknown";
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }


    /**
     * 打印请求信息
     * @author wangsong
     * @param adminlog
     * @date 2020/11/9 0009 16:33
     * @return void
     * @version 1.0.1
     */
    private void printLog(Log adminlog) {
        // 控制台打印
        log.info("user:[{}] uri:[{}] param:[{}]: ip:[{}]", adminlog.getFullName(), adminlog.getUrl(), adminlog.getRequestData(), adminlog.getIp());
    }

}



